﻿using System;
using System.Collections.Generic;
using System.Data.Objects;
using System.Linq;
using System.Text;
using RiaServicesContrib;

namespace EntityGraph.SQL
{
    /// <summary>
    /// Class that represents a graph of TypeTree edges
    /// </summary>
    internal class TypeTreeGraph : List<TypeTreeEdge>
    {
        public TypeTreeGraph(ObjectContext ctx, EntityGraphShape shape)
        {
            var edges = new List<TypeTreeEdge>();
            foreach(var edge in shape)
            {
                var tt_edge = new TypeTreeEdge
                {
                    From = TypeTree.Create(ctx, edge.FromType),
                    Edge = edge,
                    To = TypeTree.Create(ctx, edge.ToType)
                };
                edges.Add(tt_edge);
            }
            Unify(edges);
            
            foreach(var edge in edges.OrderBy(e=>e, new EdgeComparer()))
            {
                this.Add(edge);
            }
        }
        private class EdgeComparer : Comparer<TypeTreeEdge>
        {
            override public int Compare(TypeTreeEdge x, TypeTreeEdge y)
            {
                if(x == y)
                    return 0;
                if(x.To == y.From)
                {
                    return -1;
                }
                if(y.To == x.From)
                {
                    return 1;
                }
                return -1;
            }
        }
        public IEnumerable<TypeTree> Nodes
        {
            get
            {
                return this.SelectMany(e => new List<TypeTree> { e.From, e.To }).Distinct();
            }
        }
        public IEnumerable<EntityGraphEdge> Edges
        {
            get
            {
                return this.Select(e => e.Edge).Distinct();
            }
        }
        private void Unify(List<TypeTreeEdge> edges)
        {
            var types = edges.SelectMany(e => new List<TypeTree> { e.From, e.To }).Distinct().ToArray();
            bool isUnified = false;
            while(isUnified == false)
            {
                isUnified = true;
                for(int i = 0; i < types.Count(); i++)
                {
                    var first = types[i];
                    for(int j = 1; j < types.Count(); j++)
                    {
                        var second = types[j];
                        if(first.EntityType == second.EntityType)
                        {
                            if(first.EntityType != second.EntityType)
                            {
                                var unifiedType = TypeTree.Unify(first, second);
                                types[i] = unifiedType;
                                types[j] = unifiedType;
                                isUnified = false;
                            }
                            else
                            {
                                if(first != second)
                                {
                                    types[j] = types[i];
                                    isUnified = false;
                                }
                            }
                        }
                    }
                }
            }
            foreach(var e in edges)
            {
                e.From = types.First(t => t.EntityType == e.From.EntityType);
                e.To = types.First(t => t.EntityType == e.To.EntityType);
            }
        }
        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            foreach(var group in this.GroupBy(e=>e.From))
            {
                sb.AppendLine(String.Format("{0}({1})", group.Key.EntityType.Name, group.Key.EntityType.Name));
                foreach(var n in group)
                {
                    sb.AppendLine(String.Format("   {0}({1}:{2})", n.To.EntityType.Name, n.To.EntityType.Name, n.Edge.EdgeInfo.Name));
                }
            }
            return sb.ToString();
        }
    }
}
